import { type BaseElement, setCSS } from '@knno/jsx';
import { DICT } from '../../dict/dict';
import { knnoButton } from '../../theme/button.css';
import { Action } from '../action/action';
import { Popup, type PopupPosition, type Pos } from '../popup/popup';

export type OpenSelectOptions = {
	refer?: HTMLElement;
	focusOnClose?: HTMLElement;
	useAction?: boolean;
	focus?: boolean;
	popupPosition?: PopupPosition | Pos;
	noIndicator?: boolean;
	width?: number | string;
	minWidth?: number | string;
	maxWidth?: number | string;
	extraClass?: string;
	title?: string;
	noActionBar?: boolean;
};

export function isMobile(): boolean {
	return document.documentElement.offsetWidth <= 500;
}

type Callback = () => void;
type CloseCallback = (esc: boolean) => void;

interface AdaptivePopupHandler {
	readonly isAction: boolean;
	close(): void;
	onOpen(callback: Callback): this;
	onClose(callback: CloseCallback): this;
	onWillClose(callback: CloseCallback): this;
	onOK(callback: Callback): this;
}

function openAction(
	container: Node,
	title: string,
	onOpen: () => void,
	onClose: (esc: boolean) => void,
	onWillClose: (esc: boolean) => void,
	onOK: () => void,
	extraClass?: string,
	noBar?: boolean,
) {
	Action.show(container)
		.addClass(extraClass ?? '')
		.onOpen(onOpen)
		.onClose((result) => {
			onClose(result.esc ?? false);
		})
		.onWillClose((result) => {
			onWillClose(result.esc ?? false);
		})
		.setTitle(title)
		.onOK(() => {
			onOK();
		})
		.setNoBar(!!noBar);
	return {
		close(esc = false) {
			Action.close(esc);
		},
	};
}

function toWidth(value: number | string | undefined, defaultValue?: string): string | undefined {
	if (typeof value === 'number') {
		return value + 'px';
	} else {
		return value ?? defaultValue;
	}
}

function openPopup(
	refer: Element,
	container: BaseElement,
	position: PopupPosition | Pos,
	noIndicator: boolean,
	focus: boolean,
	width: number | string | undefined,
	minWidth: number | string | undefined,
	maxWidth: number | string | undefined,
	onOpen: () => void,
	onClose: (esc: boolean) => void,
	onWillClose: (esc: boolean) => void,
	extraClass?: string,
) {
	setCSS(container, {
		width: toWidth(width, refer.clientWidth + 'px'),
		minWidth: toWidth(minWidth, '8em'),
		maxWidth: toWidth(maxWidth),
	});
	const pop = Popup.create(container)
		.noIndicator(noIndicator)
		.addClass(extraClass ?? '')
		.onOpen(onOpen)
		.onClose(onClose)
		.onWillClose(onWillClose)
		.show(refer, position, focus);
	return {
		close() {
			pop.close();
		},
	};
}

export const Selector = {
	show(
		container: BaseElement,
		{
			refer,
			focusOnClose,
			useAction = false,
			focus = true,
			popupPosition = 'bottom-left',
			width,
			minWidth,
			maxWidth,
			extraClass = 'no-h-padding',
			title,
			noActionBar = false,
			noIndicator = false,
		}: OpenSelectOptions,
	): AdaptivePopupHandler {
		let onOpen: Callback | undefined;
		let onOk: Callback | undefined;
		let onClose: CloseCallback | undefined;
		let onWillClose: CloseCallback | undefined;
		let pop: { close: () => void };
		function makeBox() {
			return (
				<div style={{ display: 'flex', flexDirection: 'column' }}>
					{container}
					<button
						type="button"
						class={[knnoButton, 'primary']}
						style={{ margin: '1em 1em 0 1em' }}
						onClick={() => {
							onOk?.();
							if (focusOnClose) {
								setTimeout(() => {
									focusOnClose?.focus();
								}, 16);
							}
						}}
					>
						{DICT.ok}
					</button>
				</div>
			) as BaseElement;
		}
		let isAction = false;
		if ((isMobile() && useAction) || refer === undefined) {
			isAction = true;
			pop = openAction(
				container,
				title ?? '',
				() => {
					onOpen?.();
				},
				(esc) => {
					onClose?.(esc);
					if (esc && focusOnClose) {
						setTimeout(() => {
							focusOnClose?.focus();
						});
					}
				},
				(esc) => {
					onWillClose?.(esc);
				},
				() => {
					onOk?.();
					if (focusOnClose) {
						setTimeout(() => {
							focusOnClose?.focus();
						});
					}
				},
				extraClass,
				noActionBar,
			);
		} else {
			const box = noActionBar ? container : makeBox();
			pop = openPopup(
				refer,
				box,
				popupPosition,
				noIndicator,
				focus,
				width,
				minWidth,
				maxWidth,
				() => {
					onOpen?.();
				},
				(esc) => {
					onClose?.(esc);
					if (esc && focusOnClose) {
						setTimeout(() => {
							focusOnClose?.focus();
						});
					}
				},
				(esc) => {
					onWillClose?.(esc);
				},
				extraClass,
			);
		}
		return {
			isAction,
			...pop,
			onClose(callback) {
				onClose = callback;
				return this;
			},
			onWillClose(callback) {
				onWillClose = callback;
				return this;
			},
			onOpen(callback) {
				onOpen = callback;
				return this;
			},
			onOK(callback) {
				onOk = callback;
				return this;
			},
		};
	},
};
